using UnityEngine;

namespace FShaderInspector
{
    public partial class FShaderGUI
    {
        /// <summary> 设置表面模式 </summary>
        /// <param name="mat">目标材质球</param>
        static void SetSurfaceMode(Material mat)
        {
            var queueTag = mat.GetTag(CommonTags.Queue, true);
            var queueOffset = (int)TryGetMatFloat(mat, CommonProperty.QueueOffset);

            if (TryGetMatFloatAsBool(mat, CommonProperty.SurfaceType))
            {
                // 关闭深度写入
                mat.SetFloat(CommonProperty.ZWrite, 0);

                // 开启透明相关的宏定义
                TryEnableKeyword(mat, CommonKeyword.SurfaceTypeTransparent);

                // 关闭不透明相关的 渲染pass
                if (mat.FindPass(CommonPassName.DepthOnly) != -1)
                    mat.SetShaderPassEnabled(CommonPassName.DepthOnly, false);
                if (mat.FindPass(CommonPassName.ShadowCaster) != -1)
                    mat.SetShaderPassEnabled(CommonPassName.ShadowCaster, false);

                // 设置混合状态
                var blend = (BlendMode)TryGetMatFloat(mat, CommonProperty.BlendMode);
                var preserveSpecular = TryGetMatFloatAsBool(mat, CommonProperty.BlendModePreserveSpecular);
                SetupBlendMode(mat, blend, preserveSpecular);

                // 透明模式 AlphaToMask == 0
                SetMatBoolAsFloat(mat, CommonProperty.AlphaToMask, false);

                SetTransparentQueueTags(mat, queueTag, queueOffset);
            }
            else
            {
                // 开启深度写入
                mat.SetFloat(CommonProperty.ZWrite, 1);

                // 恢复不透明混合模式
                mat.SetFloat(CommonProperty.SurfaceType, 0);
                mat.SetFloat(CommonProperty.SrcBlend, 1);
                mat.SetFloat(CommonProperty.SrcBlendAlpha, 1);
                mat.SetFloat(CommonProperty.DstBlend, 0);
                mat.SetFloat(CommonProperty.DstBlendAlpha, 0);

                // 关闭透明相关的宏定义
                TryDisableKeyword(mat, CommonKeyword.SurfaceTypeTransparent);
                TryDisableKeyword(mat, CommonKeyword.AlphaPreMultiplyOn);
                TryDisableKeyword(mat, CommonKeyword.AlphaModulate);

                // 开启不透明相关的 渲染pass
                if (mat.FindPass(CommonPassName.DepthOnly) != -1)
                    mat.SetShaderPassEnabled(CommonPassName.DepthOnly, true);
                if (mat.FindPass(CommonPassName.ShadowCaster) != -1)
                    mat.SetShaderPassEnabled(CommonPassName.ShadowCaster, true);

                // 不透明模式 AlphaToMask == AlphaClip
                var alphaClip = TryGetMatFloatAsBool(mat, CommonProperty.AlphaClip);
                SetMatBoolAsFloat(mat, CommonProperty.AlphaToMask, alphaClip);

                SetOpaqueQueueTags(mat, TryGetMatFloatAsBool(mat, CommonProperty.AlphaClip), queueTag, queueOffset);
            }
        }

        /// <summary> 设置混合模式 </summary>
        /// <param name="mat">目标材质球</param>
        static void SetBlendMode(Material mat)
        {
            var blend = (BlendMode)mat.GetFloat(CommonProperty.BlendMode);
            var specularLighting = TryGetMatFloatAsBool(mat, CommonProperty.BlendModePreserveSpecular);
            SetupBlendMode(mat, blend, specularLighting);
        }

        /// <summary> 设置保留镜面照明 </summary>
        /// <param name="mat">目标材质球</param>
        static void SetPreserveSpecularLighting(Material mat)
        {
            var blendMode = (BlendMode)mat.GetFloat(CommonProperty.BlendMode);
            bool preserveSpecular = TryGetMatFloatAsBool(mat, CommonProperty.BlendModePreserveSpecular);

            if (blendMode == BlendMode.Alpha)
            {
                mat.SetFloat(CommonProperty.SrcBlend, preserveSpecular ? 1 : 5);
                mat.SetFloat(CommonProperty.DstBlend, 10);
                mat.SetFloat(CommonProperty.SrcBlendAlpha, 1);
                mat.SetFloat(CommonProperty.DstBlendAlpha, 10);
            }
            else if (blendMode == BlendMode.Additive)
            {
                mat.SetFloat(CommonProperty.SrcBlend, preserveSpecular ? 1 : 5);
                mat.SetFloat(CommonProperty.DstBlend, 1);
                mat.SetFloat(CommonProperty.SrcBlendAlpha, 1);
                mat.SetFloat(CommonProperty.DstBlendAlpha, 1);
            }
        }

        /// <summary> 设置 Alpha 裁剪 </summary>
        /// <param name="mat">目标材质球</param>
        static void SetAlphaClipping(Material mat)
        {
            var queueTag = mat.GetTag(CommonTags.Queue, true);
            var queueOffset = (int)TryGetMatFloat(mat, CommonProperty.QueueOffset);

            // 不透明模式 启用裁剪需要切换 Queue 和 RenderType
            if (TryGetMatFloatAsBool(mat, CommonProperty.SurfaceType))
            {
                // 设置宏状态
                if (TryGetMatFloatAsBool(mat, CommonProperty.AlphaClip))
                    TryEnableKeyword(mat, CommonKeyword.AlphaTestOn);
                else
                    TryDisableKeyword(mat, CommonKeyword.AlphaTestOn);

                SetTransparentQueueTags(mat, queueTag, queueOffset);

                // 透明模式 AlphaToMask == 0
                SetMatBoolAsFloat(mat, CommonProperty.AlphaToMask, false);
            }
            else
            {
                if (TryGetMatFloatAsBool(mat, CommonProperty.AlphaClip))
                {
                    TryEnableKeyword(mat, CommonKeyword.AlphaTestOn);
                    SetOpaqueQueueTags(mat, true, queueTag, queueOffset);
                }
                else
                {
                    TryDisableKeyword(mat, CommonKeyword.AlphaTestOn);
                    SetOpaqueQueueTags(mat, false, queueTag, queueOffset);
                }

                // 不透明模式 AlphaToMask == AlphaClip
                var newAlphaClip = TryGetMatFloatAsBool(mat, CommonProperty.AlphaClip);
                SetMatBoolAsFloat(mat, CommonProperty.AlphaToMask, newAlphaClip);
            }
        }

        /// <summary> 设置接收阴影 </summary>
        /// <param name="mat">目标材质球</param>
        static void SetReceiveShadows(Material mat)
        {
            if (TryGetMatFloatAsBool(mat, CommonProperty.ReceiveShadows))
                TryDisableKeyword(mat, CommonKeyword.ReceiveShadowsOff);
            else
                TryEnableKeyword(mat, CommonKeyword.ReceiveShadowsOff);
        }

        /// <summary> 设置混合模式 </summary>
        /// <param name="mat">设置的材质球</param> <param name="blendMode">混合模式</param> <param name="specularLighting">混合模式保持高光</param>
        static void SetupBlendMode(Material mat, BlendMode blendMode, bool specularLighting)
        {
            // 透明模式关闭深度写入
            mat.SetFloat(CommonProperty.ZWrite, 0);

            // 设置混合状态
            switch (blendMode)
            {
                case BlendMode.Alpha:
                    mat.SetFloat(CommonProperty.BlendMode, 0);

                    if (specularLighting)
                    {
                        // 混合模式保存高光混合模式
                        mat.SetFloat(CommonProperty.SrcBlend, 1);
                        mat.SetFloat(CommonProperty.SrcBlendAlpha, 1);
                        mat.SetFloat(CommonProperty.DstBlend, 10);
                        mat.SetFloat(CommonProperty.DstBlendAlpha, 10);
                    }
                    else
                    {
                        // 默认标准模式
                        mat.SetFloat(CommonProperty.SrcBlend, 5);
                        mat.SetFloat(CommonProperty.SrcBlendAlpha, 1);
                        mat.SetFloat(CommonProperty.DstBlend, 10);
                        mat.SetFloat(CommonProperty.DstBlendAlpha, 10);
                    }
                    break;
                case BlendMode.Premultiply:
                    mat.SetFloat(CommonProperty.BlendMode, 1);
                    mat.SetFloat(CommonProperty.SrcBlend, 1);
                    mat.SetFloat(CommonProperty.SrcBlendAlpha, 1);
                    mat.SetFloat(CommonProperty.DstBlend, 10);
                    mat.SetFloat(CommonProperty.DstBlendAlpha, 10);
                    break;
                case BlendMode.Additive:
                    mat.SetFloat(CommonProperty.BlendMode, 2);

                    if (specularLighting)
                    {
                        // 混合模式保存高光混合模式
                        mat.SetFloat(CommonProperty.SrcBlend, 1);
                        mat.SetFloat(CommonProperty.SrcBlendAlpha, 1);
                        mat.SetFloat(CommonProperty.DstBlend, 1);
                        mat.SetFloat(CommonProperty.DstBlendAlpha, 1);
                    }
                    else
                    {
                        // 默认标准模式
                        mat.SetFloat(CommonProperty.SrcBlend, 5);
                        mat.SetFloat(CommonProperty.SrcBlendAlpha, 1);
                        mat.SetFloat(CommonProperty.DstBlend, 1);
                        mat.SetFloat(CommonProperty.DstBlendAlpha, 1);
                    }
                    break;
                case BlendMode.Multiply:
                    mat.SetFloat(CommonProperty.BlendMode, 3);
                    mat.SetFloat(CommonProperty.SrcBlend, 2);
                    mat.SetFloat(CommonProperty.SrcBlendAlpha, 0);
                    mat.SetFloat(CommonProperty.DstBlend, 0);
                    mat.SetFloat(CommonProperty.DstBlendAlpha, 1);
                    break;
            }

            // Multiply 混合模式宏设置
            if (blendMode == BlendMode.Multiply)
                TryEnableKeyword(mat, CommonKeyword.AlphaModulate);
            else
                TryDisableKeyword(mat, CommonKeyword.AlphaModulate);

            // Alpha Additive 混合模式宏设置
            if (blendMode == BlendMode.Alpha || blendMode == BlendMode.Additive)
            {
                if (specularLighting)
                    TryEnableKeyword(mat, CommonKeyword.AlphaPreMultiplyOn);
                else
                    TryDisableKeyword(mat, CommonKeyword.AlphaPreMultiplyOn);
            }
            else
            {
                TryDisableKeyword(mat, CommonKeyword.AlphaPreMultiplyOn);
            }
        }

        /// <summary>设置透明队列 Tags </summary>
        /// <param name="mat">材质</param> <param name="queueTag">标签</param> <param name="queueOffset">队列偏移</param>
        static void SetTransparentQueueTags(Material mat, string queueTag, int queueOffset)
        {
            // 设置队列层级
            // 默认 RenderQueue = 3000
            // 默认 RenderType  = Transparent
            if (queueTag == CommonTags.Queue_Transparent && queueOffset == 0)
                mat.renderQueue = -1;
            else
                mat.renderQueue = 3000 + (int)TryGetMatFloat(mat, CommonProperty.QueueOffset);

            mat.SetOverrideTag(CommonTags.RenderType, CommonTags.RenderType_Transparent);
        }

        /// <summary> 设置不透明队列 Tags </summary>
        /// <param name="mat">材质</param> <param name="alphaClip">裁剪</param> <param name="queueTag">标签</param> <param name="queueOffset">队列偏移</param>
        static void SetOpaqueQueueTags(Material mat, bool alphaClip, string queueTag, int queueOffset)
        {
            if (alphaClip)
            {
                // 裁剪 RenderQueue = 2450
                // 裁剪 RenderType  = TransparentCutout
                if (queueTag == CommonTags.Queue_AlphaTest && queueOffset == 0)
                    mat.renderQueue = -1;
                else
                    mat.renderQueue = 2450 + (int)TryGetMatFloat(mat, CommonProperty.QueueOffset);

                mat.SetOverrideTag(CommonTags.RenderType, CommonTags.RenderType_TransparentCutout);
            }
            else
            {
                // 默认 RenderQueue = 2000
                // 默认 RenderType  = Opaque
                if (string.IsNullOrEmpty(queueTag) && queueOffset == 0)
                    mat.renderQueue = -1;
                else
                    mat.renderQueue = 2000 + (int)TryGetMatFloat(mat, CommonProperty.QueueOffset);

                mat.SetOverrideTag(CommonTags.RenderType, CommonTags.RenderType_Opaque);
            }
        }

        /// <summary> 尝试获取材质 float 值，未获取时返回 notValue 默认 0 </summary>
        /// <param name="mat">目标材质球</param> <param name="propName">属性名称</param> <param name="notValue">未获取返回值</param> <returns>float</returns>
        static float TryGetMatFloat(Material mat, string propName, int notValue = 0)
        {
            if (mat.HasProperty(propName))
                return mat.GetFloat(propName);

            return notValue;
        }

        /// <summary> 尝试获取材质 float 转换 bool 值，未获取时返回 notValue 默认 false </summary>
        /// <param name="mat">目标材质球</param> <param name="propName">属性名称</param> <param name="notValue">未获取返回值</param> <returns>bool</returns>
        static bool TryGetMatFloatAsBool(Material mat, string propName, bool notValue = false)
        {
            if (mat.HasProperty(propName))
                return (int)mat.GetFloat(propName) == 1;

            return notValue;
        }

        /// <summary> 设置材质 bool 转换 float值 </summary>
        /// <param name="mat">目标材质球</param> <param name="propName">属性名称</param> <param name="enable">启用</param>
        static void SetMatBoolAsFloat(Material mat, string propName, bool enable) => mat.SetFloat(propName, enable ? 1 : 0);
    }
}